package com.xjrsoft.common.advice;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.*;
import cn.hutool.db.Entity;
import cn.hutool.extra.servlet.ServletUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.enums.WorkflowIsRecycleType;
import com.xjrsoft.common.enums.YesOrNoEnum;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.model.result.R;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.module.workflow.constant.WorkflowConstant;
import com.xjrsoft.module.workflow.entity.WorkflowDraft;
import com.xjrsoft.module.workflow.entity.WorkflowExtra;
import com.xjrsoft.module.workflow.entity.WorkflowFormRelation;
import com.xjrsoft.module.workflow.entity.WorkflowSchema;
import com.xjrsoft.module.workflow.model.WorkflowListResult;
import com.xjrsoft.module.workflow.service.IWorkflowDraftService;
import com.xjrsoft.module.workflow.service.IWorkflowExtraService;
import com.xjrsoft.module.workflow.service.IWorkflowFormRelationService;
import com.xjrsoft.module.workflow.service.IWorkflowSchemaService;
import javassist.*;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流 模板 绑定菜单 返回值新增工作流的相关属性
 *
 * @Author: tzx
 * @Date: 2023/3/27 14:24
 */
@Slf4j
@RestControllerAdvice
@AllArgsConstructor
public class WorkflowResponseBodyAdvice implements ResponseBodyAdvice<Object> {


    private RedisUtil redisUtil;

    private IWorkflowFormRelationService formRelationService;

    private HistoryService historyService;

    private IWorkflowSchemaService workflowSchemaService;

    private final IWorkflowDraftService workflowDraftService;

    private final IWorkflowExtraService workflowExtraService;

    private TaskService taskService;

    private static final Map<String, Constructor<?>> cacheClass = new HashMap<>();

    private static final String FORM_ID_KEY = "FormId";

    private static final String KEY_VALUE_KEY = "PK";

    private static final String FIELD_STRING = "private Object workflowData;";

    private static final String GET_MOTHOD_STRING = "public Object getWorkflowData(){ return this.workflowData; }";

    private static final String RESULT_KEY = "workflowData";

    @Override
    public boolean supports(@NonNull MethodParameter returnType, @NonNull Class converterType) {
        return true;
    }


    @Override
    public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType, @NonNull Class selectedConverterType, @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {

        if (!(body instanceof R)) {
            return body;
        }

        if (((R) body).getData() == null) {
            return body;
        }

        //先从header里面获取formId  如果能获取到 就证明是自定义表单
        String headerFormId = ServletUtil.getHeader(((ServletServerHttpRequest) request).getServletRequest(), FORM_ID_KEY, CharsetUtil.UTF_8);
        String headerKey = ServletUtil.getHeader(((ServletServerHttpRequest) request).getServletRequest(), KEY_VALUE_KEY, CharsetUtil.UTF_8);


        //如果是自定义表单 需要获取 工作流数据
        if (StrUtil.isNotBlank(headerFormId)) {
            if (ClassUtil.equals(((R) body).getData().getClass(), PageOutput.class.getName(), true)) {
                return formTemplateGetWorkflowDataHandler(body, headerFormId, headerKey);
            }
        }


        String formId = ((ServletServerHttpRequest) request).getServletRequest().getParameter(FORM_ID_KEY);

        if (StrUtil.isBlank(formId)) {
            return body;
        }
        String key = ((ServletServerHttpRequest) request).getServletRequest().getParameter(KEY_VALUE_KEY);

        if (StrUtil.isBlank(key)) {
            return body;
        }

        //如果是代码生成器表单 需要获取 工作流数据
        return codeGetWorkflowDataHandler(body, formId, key);
    }

    private Object formTemplateGetWorkflowDataHandler(Object body, String headerFormId, String headerKey) {

        List<Object> keyValueFormList = new ArrayList<>();
        List<?> listData = null;
        if (ClassUtil.equals(((R) body).getData().getClass(), PageOutput.class.getName(), true)) {
            Object result = Convert.convert(R.class, body).getData();
            PageOutput<?> pageOutput = Convert.convert(PageOutput.class, result);
            listData = pageOutput.getList();
            if (listData == null || listData.size() == 0) {
                return body;
            }

        } else if (ClassUtil.equals(((R) body).getData().getClass(), ArrayList.class.getName(), true)) {
            Object result = Convert.convert(R.class, body).getData();
            listData = Convert.convert(List.class, result);
            if (listData == null || listData.size() == 0) {
                return body;
            }
        } else {
            return body;
        }

        for (Object data : listData) {
            Entity rowValue = Convert.convert(Entity.class, data);
            Object keyValue = rowValue.get(headerKey);
            keyValueFormList.add(keyValue);
        }

        WorkflowSchema schema = null;
        List<WorkflowSchema> schemaList = workflowSchemaService.list(Wrappers.lambdaQuery(WorkflowSchema.class)
                .eq(WorkflowSchema::getFormId, Long.parseLong(headerFormId)).select(WorkflowSchema::getId, WorkflowSchema::getActivityFlag));
        if (schemaList.size() > 1) {//如果有多条，就获取到当前版本为活跃的流程模板
            schema = schemaList.stream().filter(x -> ObjectUtil.isNotNull(x.getActivityFlag()) && x.getActivityFlag().equals(YesOrNoEnum.YES.getCode())).findFirst().orElse(new WorkflowSchema());
        } else if (schemaList.size() == 1) {
            schema = schemaList.get(0);
        }
        //获取到所有关联的 流程id
        List<WorkflowFormRelation> relations = formRelationService.list(Wrappers.lambdaQuery(WorkflowFormRelation.class).eq(WorkflowFormRelation::getFormId, Long.parseLong(headerFormId)).in(WorkflowFormRelation::getFormKeyValue, keyValueFormList).select(WorkflowFormRelation::getProcessId, WorkflowFormRelation::getFormKeyValue));

        Set<String> processIds = relations.stream().map(WorkflowFormRelation::getProcessId).collect(Collectors.toSet());
        List<HistoricProcessInstance> processInstanceList;
        if (processIds.size() == 0) {
            processInstanceList = new ArrayList<>();
        } else {
            processInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processIds).list();
        }
        // 查询草稿信息
        List<WorkflowDraft> draftList = workflowDraftService.list(Wrappers.<WorkflowDraft>query().lambda().select(WorkflowDraft::getId).eq(WorkflowDraft::getCreateUserId, StpUtil.getLoginIdAsLong()));

        //找到当前所有流程的 所在执行的任务list
        String[] processIdArrays = relations.stream().map(WorkflowFormRelation::getProcessId).toArray(String[]::new);

        List<Task> taskList;
        if (processIdArrays.length > 0) {
            taskList = taskService.createTaskQuery()
                    .active()
                    .processInstanceIdIn(processIdArrays)
                    .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                    .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                    .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + StpUtil.getLoginIdAsString() + StringPool.PERCENT)
                    .list();
        } else {
            taskList = new ArrayList<>();
        }

        for (Object data : listData) {
            Entity rowValue = Convert.convert(Entity.class, data);
            Object keyValue = rowValue.get(headerKey);

            List<WorkflowFormRelation> collect = relations.stream().filter(x -> x.getFormKeyValue().equals(keyValue)).collect(Collectors.toList());

            WorkflowListResult workflowListResult = new WorkflowListResult();

            if (collect.size() > 0) {
                //获取最新的一个工作流关联数据，防止重新发起的多条数据获取到已经结束的流程数据
                WorkflowFormRelation workflowFormRelation = collect.get(collect.size() - 1);

                HistoricProcessInstance historicProcessInstance = processInstanceList.stream().filter(p -> p.getId().equals(workflowFormRelation.getProcessId())).findFirst().orElse(null);

                if (ObjectUtil.isNotNull(historicProcessInstance)) {
                    assert historicProcessInstance != null;
                    workflowListResult.setStatus(historicProcessInstance.getState());
                    workflowListResult.setProcessId(historicProcessInstance.getId());
                }

                //获取到当前数据 所关联的 的流程 正在执行的 任务
                List<Task> thisDataTask = taskList.stream().filter(x -> x.getProcessInstanceId().equals(workflowFormRelation.getProcessId())).collect(Collectors.toList());
                if (thisDataTask.size() > 0) {
                    workflowListResult.setTaskIds(thisDataTask.stream().map(Task::getId).collect(Collectors.toList()));
                }

            }

            if (ObjectUtil.isNotNull(schema)) {
                workflowListResult.setSchemaId(schema.getId());
                // 查询草稿信息
                WorkflowDraft draft = null;
                List<String> taskIds = workflowListResult.getTaskIds();
                for (WorkflowDraft d : draftList) {
                    if (CollectionUtils.isNotEmpty(taskIds) && StrUtil.equals(d.getTaskId(), taskIds.get(0))
                            || StrUtil.equals(d.getDataId(), String.valueOf(keyValue)) && schema.getId().equals(d.getSchemaId())) {
                        draft = d;
                    }
                }
                if (draft != null) workflowListResult.setDraftId(draft.getId());
            } else {
                workflowListResult.setEnabled(Boolean.FALSE);
            }

            rowValue.set(RESULT_KEY, workflowListResult);
        }
        return body;
    }

    private Object codeGetWorkflowDataHandler(Object body, String formId, String key) {
        WorkflowSchema schema = null;
        List<WorkflowSchema> schemaList = workflowSchemaService.list(Wrappers.lambdaQuery(WorkflowSchema.class)
                .eq(WorkflowSchema::getFormId, Long.parseLong(formId)).select(WorkflowSchema::getId, WorkflowSchema::getActivityFlag));
        if (schemaList.size() > 1) {//如果有多条，就获取到当前版本为活跃的流程模板
            schema = schemaList.stream().filter(x -> ObjectUtil.isNotNull(x.getActivityFlag()) && x.getActivityFlag().equals(YesOrNoEnum.YES.getCode())).findFirst().orElse(new WorkflowSchema());
        } else if (schemaList.size() == 1) {
            schema = schemaList.get(0);
        }
        List<Object> keyValueList = new ArrayList<>();

        List<?> listData;

        //判断是否为分页返回值
        boolean isPage = ClassUtil.equals(((R) body).getData().getClass(), PageOutput.class.getName(), true);
        R r = Convert.convert(R.class, body);
        Object resultData = r.getData();
        if (isPage) {
            PageOutput<?> pageOutput = Convert.convert(PageOutput.class, resultData);
            listData = pageOutput.getList();
            if (listData == null || listData.size() == 0) {
                return body;
            }

            for (Object data : listData) {
                Object keyValue = ReflectUtil.getFieldValue(data, key);
                keyValueList.add(keyValue.toString());
            }
        }
        //判断是否为不分页列表返回值
        else if (ClassUtil.equals(((R) body).getData().getClass(), ArrayList.class.getName(), true)) {
            listData = Convert.convert(List.class, resultData);

            if (listData == null || listData.size() == 0) {
                return body;
            }

            for (Object data : listData) {
                Object keyValue = ReflectUtil.getFieldValue(data, key);
                keyValueList.add(keyValue.toString());
            }
        } else {
            return body;
        }


        //获取到所有关联的 流程id
        List<WorkflowFormRelation> relations = formRelationService.list(Wrappers.lambdaQuery(WorkflowFormRelation.class).eq(WorkflowFormRelation::getFormId, Long.parseLong(formId)).in(WorkflowFormRelation::getFormKeyValue, keyValueList).select(WorkflowFormRelation::getProcessId, WorkflowFormRelation::getFormKeyValue));
//        if (relations.size() == 0) {
//            return body;
//        }
        Set<String> processIds = relations.stream().map(WorkflowFormRelation::getProcessId).collect(Collectors.toSet());
        List<HistoricProcessInstance> processInstanceList;
        if (processIds.size() == 0) {
            processInstanceList = new ArrayList<>();
        } else {
            processInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processIds).list();
        }

        //找到当前所有流程的 所在执行的任务list
        String[] processIdArrays = relations.stream().map(WorkflowFormRelation::getProcessId).toArray(String[]::new);

        List<Task> taskList;
        if (processIdArrays.length > 0) {
            taskList = taskService.createTaskQuery()
                    .active()
                    .processInstanceIdIn(processIdArrays)
                    .taskVariableValueEquals(WorkflowConstant.TASK_IS_APPOINT_APPROVE, YesOrNoEnum.NO.getCode())
                    .processVariableValueEquals(WorkflowConstant.PROCESS_ISRECYCLE_FLAG_KEY, WorkflowIsRecycleType.NO.getCode())
                    .taskVariableValueLike(WorkflowConstant.TASK_ASSIGNEE_VAR_KEY, StringPool.PERCENT + StpUtil.getLoginIdAsString() + StringPool.PERCENT)
                    .list();
        } else {
            taskList = new ArrayList<>();
        }

        ClassPool pool = new ClassPool(true);

        pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
        List<Object> result = new ArrayList<>();
        try {
            CtClass ctClass = pool.get(listData.get(0).getClass().getName());

            try {
                CtField workflowField = CtField.make(FIELD_STRING, ctClass);

                ctClass.addField(workflowField);

                CtMethod workflowMethod = CtMethod.make(GET_MOTHOD_STRING, ctClass);
                ctClass.addMethod(workflowMethod);

            } catch (CannotCompileException e) {
                e.printStackTrace();
            }

            Constructor<?> declaredConstructor;
            if (!cacheClass.containsKey(ctClass.getName())) {
                declaredConstructor = ctClass.toClass().getDeclaredConstructor();
                cacheClass.put(ctClass.getName(), declaredConstructor);
            } else {
                declaredConstructor = cacheClass.get(ctClass.getName());
            }


            List<String> allTaskIds = taskList.stream().map(Task::getId).collect(Collectors.toList());
            List<WorkflowExtra> taskExtraList = workflowExtraService.list(Wrappers.lambdaQuery(WorkflowExtra.class).select(WorkflowExtra::getTaskId, WorkflowExtra::getSerialNumber).in(ObjectUtil.isNotEmpty(allTaskIds), WorkflowExtra::getTaskId, allTaskIds));

            for (Object data : listData) {
                Object keyValue = ReflectUtil.getFieldValue(data, key);
                //keyValue.toString是采购申请时使用的，因为采购表存的id是Long类型的
                List<WorkflowFormRelation> collect = relations.stream().filter(x -> x.getFormKeyValue().equals(keyValue) || x.getFormKeyValue().equals(keyValue.toString())).collect(Collectors.toList());

                Object newObject = declaredConstructor.newInstance();

                result.add(newObject);
                BeanUtil.copyProperties(data, newObject);

                WorkflowListResult workflowListResult = new WorkflowListResult();

                if (collect.size() > 0) {
                    //获取最新的一个工作流关联数据，防止重新发起的多条数据获取到已经结束的流程数据
                    WorkflowFormRelation relation = collect.get(collect.size() - 1);

                    HistoricProcessInstance historicProcessInstance = processInstanceList.stream().filter(p -> p.getId().equals(relation.getProcessId())).findFirst().orElse(null);

                    if (ObjectUtil.isNotNull(historicProcessInstance)) {
                        assert historicProcessInstance != null;
                        workflowListResult.setStatus(historicProcessInstance.getState());
                        workflowListResult.setProcessId(historicProcessInstance.getId());
                    }

                    //获取到当前数据 所关联的 的流程 正在执行的 任务
                    List<Task> thisDataTask = taskList.stream().filter(x -> x.getProcessInstanceId().equals(relation.getProcessId())).collect(Collectors.toList());
                    if (thisDataTask.size() > 0) {
                        workflowListResult.setTaskIds(thisDataTask.stream().map(Task::getId).collect(Collectors.toList()));
                    }
                }

                Optional<WorkflowExtra> extraOptional = taskExtraList.stream().filter(x -> workflowListResult.getTaskIds().contains(x.getTaskId())).findFirst();

                extraOptional.ifPresent(x -> {
                    workflowListResult.setSerialNumber(x.getSerialNumber());
                });

                if (ObjectUtil.isNotNull(schema)) {
                    workflowListResult.setSchemaId(schema.getId());
                    // 查询草稿信息
                    LambdaQueryWrapper<WorkflowDraft> wrapper = Wrappers.<WorkflowDraft>query().lambda().select(WorkflowDraft::getId)
                            .eq(WorkflowDraft::getCreateUserId, StpUtil.getLoginIdAsLong())
                            .eq(WorkflowDraft::getDataId, String.valueOf(keyValue));
                    List<String> taskIds = workflowListResult.getTaskIds();
                    if (CollectionUtils.isNotEmpty(taskIds)) {
                        wrapper.eq(WorkflowDraft::getTaskId, taskIds.get(0));
                    } else {
                        wrapper.eq(WorkflowDraft::getSchemaId, schema.getId());
                    }
                    WorkflowDraft draft = workflowDraftService.getOne(wrapper, false);
                    if (draft != null) workflowListResult.setDraftId(draft.getId());
                } else {
                    workflowListResult.setEnabled(Boolean.FALSE);
                }


                ReflectUtil.setFieldValue(newObject, RESULT_KEY, workflowListResult);
            }

            ctClass.detach();
        } catch (NotFoundException | CannotCompileException | NoSuchMethodException | InvocationTargetException |
                 InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new MyException("表单发起流程数据转换出错，请联系管理员！");
        }

        if (isPage) {
            ((PageOutput) (r.getData())).setList(result);
        } else {
            r.put("data", result);
        }

        return body;
    }

}
